#pragma once

#include "LogManagement.h"
#include "MemoryBase.h"
#include "ContainerBase.h"

namespace EngineLib {

	class ListItem
	{
	public:
		void *_data;
		ListItem *_prev;
		ListItem *_next;
	};

	template <class T>
	class List
	{
	private:
		ListItem *_frist;
		ListItem *_last;
	public:
		class Itr {
		private:
			ListItem *_itr_current;
		public:
			Itr(void) : _itr_current(0) {}
			Itr(ListItem *current_item) : _itr_current(current_item) {}
			~Itr(void) {}

			void setCurrentItem(ListItem *item)
			{
				_itr_current = item;
			}

			ListItem* getCurrentItem(void)
			{
				return _itr_current;
			}

			void Prev(void)
			{
				if (_itr_current)
					_itr_current = _itr_current->_prev;
			}

			void Next(void)
			{
				if (_itr_current)
					_itr_current = _itr_current->_next;
			}

			T* Data(void)
			{
				return _itr_current ? ((T*)(_itr_current->_data)) : 0;
			}

			bool IsValid(void)
			{
				return !!_itr_current;
			}
		};
	public:
		List(void) : _frist(0), _last(0) {}
		~List(void) {}

		static List<T>* New(void)
		{
			List<T>* rst = (List<T>*)MemoryManagement::Common128->Allocate();
			rst->_frist = 0;
			rst->_last = 0;
			return rst;
		}

		static void Delete(List<T> *list)
		{
			list->Clear();
			MemoryManagement::Common128->Free(list);
		}

		Itr FirstItr(void) const
		{
			return Itr(_frist);
		}

		Itr LastItr(void) const
		{
			return Itr(_last);
		}

		void PushBack(T *data)
		{
			ListItem *item = (ListItem*)MemoryManagement::Common128->Allocate();
			item->_data = data;
			item->_next = 0;
			if (_last) {
				_last->_next = item;
				item->_prev = _last;
				_last = item;
			}
			else {
				item->_prev = 0;
				_last = _frist = item;
			}
		}

		void PushFront(T *data)
		{
			ListItem *item = (ListItem*)MemoryManagement::Common128->Allocate();
			item->_data = data;
			item->_prev = 0;
			if (_frist) {
				_frist->_prev = item;
				item->_next = _frist;
				_frist = item;
			}
			else {
				item->_next = 0;
				_last = _frist = item;
			}
		}

		T* PopBack(void)
		{
			if (!_last)
				return 0;
			T *rst = (T*)_last->_data;
			ListItem *toFree = _last;
			_last = _last->_prev;
			if (_last)
				_last->_next = 0;
			else
				_frist = 0;
			MemoryManagement::Common128->Free(toFree);
			return rst;
		}

		T* PopFront(void)
		{
			if (!_frist)
				return 0;
			T *rst = (T*)_frist->_data;
			ListItem *toFree = _frist;
			_frist = _frist->_next;
			if (_frist)
				_frist->_prev = 0;
			else
				_last = 0;
			MemoryManagement::Common128->Free(toFree);
			return rst;
		}

		void InsertBefore(T *data, Itr &itr)
		{
			if (!itr.IsValid()) {
				LogManagement::Error(ERROR_CODE_ILLEGAL_PARAM);
				return;
			}
			ListItem *item = (ListItem*)MemoryManagement::Common128->Allocate();
			item->_data = data;
			item->_prev = itr._itr_current->_prev;
			item->_next = itr._itr_current;
			itr._itr_current->_prev->_next = item;
			itr._itr_current->_prev = item;
		}

		void InsertAfter(T *data, Itr &itr)
		{
			if (!itr.IsValid()) {
				LogManagement::Error(ERROR_CODE_ILLEGAL_PARAM);
				return;
			}
			ListItem *item = (ListItem*)MemoryManagement::Common128->Allocate();
			item->_data = data;
			item->_prev = itr.getCurrentItem();
			item->_next = itr.getCurrentItem()->_next;
			itr.getCurrentItem()->_next->_prev = item;
			itr.getCurrentItem()->_next = item;
		}

		void Erase(Itr &itr)
		{
			if (!itr.IsValid()) {
				LogManagement::Error(ERROR_CODE_ILLEGAL_PARAM);
				return;
			}
			if (itr.getCurrentItem()->_prev) {
				itr.getCurrentItem()->_prev->_next = itr.getCurrentItem()->_next;
			}
			if (itr.getCurrentItem()->_next) {
				itr.getCurrentItem()->_next->_prev = itr.getCurrentItem()->_prev;
			}
			ListItem *toFree = itr.getCurrentItem();
			itr.Next();
			MemoryManagement::Common128->Free(toFree);
		}

		void Erase(T *data)
		{
			for (Itr itr = FirstItr(); itr.IsValid(); itr.Next()) {
				if (itr.Data() == data) {
					Erase(itr);
					return;
				}
			}
		}

		void Clear(void)
		{
			ListItem *toFree = _frist;
			ListItem *next = _frist;
			while (toFree) {
				next = toFree->_next;
				MemoryManagement::Common128->Free(toFree);
				toFree = next;
			}
			_last = _frist = 0;
		}
	};
}